package com.tools.web.interceptor.auth;

import com.tools.common.object.Note;
import com.tools.common.thread.TimePair;
import com.tools.common.thread.Timeunit;
import com.tools.web.WebKit;
import com.tools.web.http.HttpDataFromEnum;
import org.slf4j.Logger;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Objects;

/**
 * 【双 token 机制】：
 * 运用 refresh_token 实现 AuthToken 续期的实现类
 * */
@Note("【双 token 机制】：" +
        "运用 refresh_token 实现 AuthToken 续期的实现类")
public final class RefreshAuthTokenExtender implements AuthTokenExtender {

    @Note("refresh_token 的名称")
    private final String refreshTokenName;

    @Note("refresh_token 所在的位置")
    private final HttpDataFromEnum refreshTokenIn;

    @Note("每次续期的时间，单位毫秒")
    private final long extendTimeMill;

    @Note("现在 - 过期时间 < 该值，就允许续期。单位毫秒")
    private final long maxAllowExtendTimeMill;

    @Note("在 maxAllowExtendTime 的期限内，允许续期多少次")
    private final int maxAllowExtendCount;

    /* **************************************************************************************
     *
     *          构造器
     *
     * **************************************************************************************
     * */

    public RefreshAuthTokenExtender(String refreshTokenName, HttpDataFromEnum refreshTokenIn, TimePair extendTime, TimePair maxAllowExtendTime, int maxAllowExtendCount) {
        this.refreshTokenName = refreshTokenName;
        this.refreshTokenIn = refreshTokenIn;
        this.extendTimeMill = extendTime != null ? extendTime.toMill() : 0;
        this.maxAllowExtendTimeMill = maxAllowExtendTime != null ? maxAllowExtendTime.toMill() : 0;
        this.maxAllowExtendCount = maxAllowExtendCount;
    }

    /* **************************************************************************************
     *
     *          Getter
     *
     * **************************************************************************************
     * */

    public String getRefreshTokenName() {
        return refreshTokenName;
    }

    public HttpDataFromEnum getRefreshTokenIn() {
        return refreshTokenIn;
    }

    @Override
    public long getExtendTimeMill() {
        return extendTimeMill;
    }

    @Override
    public long getMaxAllowExtendTimeMill() {
        return maxAllowExtendTimeMill;
    }

    @Override
    public int getMaxAllowExtendCount() {
        return maxAllowExtendCount;
    }

    @Override
    public String toString() {
        return "RefreshAuthTokenExtender{" +
                "refreshTokenName=" + refreshTokenName +
                ", refreshTokenIn=" + refreshTokenIn +
                ", extendTimeMill" + extendTimeMill +
                ", maxAllowExtendTimeMill" + maxAllowExtendTimeMill +
                ", maxAllowExtendCount" + maxAllowExtendCount +
                '}';
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        RefreshAuthTokenExtender that = (RefreshAuthTokenExtender) o;
        return Objects.equals(refreshTokenName, that.refreshTokenName) && refreshTokenIn == that.refreshTokenIn && extendTimeMill == that.extendTimeMill && maxAllowExtendTimeMill == that.maxAllowExtendTimeMill && maxAllowExtendCount== that.maxAllowExtendCount;
    }

    @Override
    public int hashCode() {
        return Objects.hash(refreshTokenName, refreshTokenIn, extendTimeMill, maxAllowExtendTimeMill, maxAllowExtendCount);
    }

    /* **************************************************************************************
     *
     *          功能方法
     *
     * **************************************************************************************
     * */

    @Override
    public boolean isEnable() {
        return refreshTokenName != null && !refreshTokenName.isEmpty()
                && refreshTokenIn != null
                && extendTimeMill > 0
                && maxAllowExtendTimeMill > 0
                && maxAllowExtendCount > 0;
    }

    @Override
    public RefreshAuthTokenExtender copy() {
        TimePair extendTime = new TimePair(extendTimeMill, Timeunit.MILL);
        TimePair maxAllowExtendTime = new TimePair(maxAllowExtendTimeMill, Timeunit.MILL);
        return new RefreshAuthTokenExtender(refreshTokenName, refreshTokenIn, extendTime, maxAllowExtendTime, maxAllowExtendCount);
    }


    @Note("双 token 机制刷新 access_token 的，并且返回加密后的 refresh_token 字符串的逻辑")
    @Override
    public boolean extend(HttpServletRequest request, HttpServletResponse response,
                          AuthenticationManager authenticationManager, Logger logger,
                          String encryptedAccessToken, AuthToken accessToken,
                          Timeunit timeunitUsedBySubclass) throws Exception {
        String encryptedRefreshToken = this.refreshTokenIn.getData(request, this.refreshTokenName);
        if(encryptedRefreshToken == null || encryptedRefreshToken.isEmpty()) {
            return false;
        }
        AuthToken refreshToken;
        try {
            refreshToken = authenticationManager.decryptAuthToken(encryptedRefreshToken);
        } catch (Exception e) {
            logger.error("双 token 机制进行 AuthToken 的续期操作出错, 原因是: {}\n原 token: {}\nrefresh token: {}",
                    e.getMessage(), encryptedAccessToken, encryptedRefreshToken);
            return false;
        }
        boolean b1 = Objects.equals(refreshToken.getUserId(), accessToken.getUserId());
        boolean b2 = Objects.equals(refreshToken.getUuid(), accessToken.getUuid());
        boolean b3 = Objects.equals(refreshToken.getLoginDeviceType(), accessToken.getLoginDeviceType());
        boolean b4 = Objects.equals(refreshToken.getLoginUserType(), accessToken.getLoginUserType());
        boolean b5 = Objects.equals(refreshToken.getLoginTimeMill(), accessToken.getLoginTimeMill());
        if(!(b1 && b2 && b3 && b4 && b5)) {
            return false;
        }
        //现在 - 过期时间已经超过最大允许的续期时间，不允许再续期
        long maxAllowExtendTimeMill = refreshToken.getExpirationMill();
        long accessTokenExpirationMill = accessToken.getExpirationMill();
        long now = System.currentTimeMillis();
        if(now - accessTokenExpirationMill >= maxAllowExtendTimeMill) {
            return false;
        }
        //如果已经续期的次数超过了最大允许阈值，也不允许续期
        int maxAllowExtendCount = refreshToken.getExtendedCount();
        int accessTokenExtendedCount = accessToken.getExtendedCount();
        if(accessTokenExtendedCount >= maxAllowExtendCount) {
            return false;
        }
        //续期
        long newExpiration = now + this.extendTimeMill;
        accessToken.setExpirationMill(newExpiration, Timeunit.MILL);
        int newExtendedCount = accessToken.getExtendedCount() + 1;
        accessToken.setExtendedCount(newExtendedCount);
        this.refreshTokenIn.setData(request, response, this.refreshTokenName, accessToken);
        WebKit.openHeader(response, this.refreshTokenName);
        return true;
    }
}
